package com.sexypsf;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.concurrent.*;

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import com.sexypsf.*;


public class MAudioPlayerThread extends Thread {

	private static final int MINE_AUDIO_BUFFER_TOTAL_LEN = 1024*256;
	private static final int MINE_AUDIO_BUFFER_PUT_GET_LEN = MINE_AUDIO_BUFFER_TOTAL_LEN/4;
	private AudioTrack m_audiotrack;
	private MineAudioBuffer m_audio_buffer;
	private boolean shallExit;
	private String m_FileName;
	
	class MinePSFPlayerPlayThread extends Thread {
		public MinePSFPlayerPlayThread(){
		}
		public void run()
		{	        
			final int DATA_LEN = MINE_AUDIO_BUFFER_PUT_GET_LEN;
	        byte[] audioData = new byte[DATA_LEN];
	        int len;
			int counter = 0;
			while(!isInterrupted())
			{
				if (shallExit) {
					m_audio_buffer.Discard();
					break;
				}
				try {
					len = m_audio_buffer.get(audioData, 0, DATA_LEN);
				} catch (InterruptedException e) {
					// No more audio data, exit the thread
					break;
				}
				System.out.println("Write data to HW: "+(counter++) +" len: "+len);
				m_audiotrack.write(audioData, 0, len);
			}
			System.out.println("PlayThread exit!");
		}
	}
	
	public MAudioPlayerThread() {
		// TODO Auto-generated constructor stub
		m_audiotrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100,
        		AudioFormat.CHANNEL_CONFIGURATION_STEREO, 
        		AudioFormat.ENCODING_PCM_16BIT, MINE_AUDIO_BUFFER_PUT_GET_LEN, AudioTrack.MODE_STREAM);
		m_audio_buffer = new MineAudioBuffer(MINE_AUDIO_BUFFER_TOTAL_LEN);
	}
	
	public MAudioPlayerThread(Runnable runnable) {
		super(runnable);
		// TODO Auto-generated constructor stub
	}

	public MAudioPlayerThread(String threadName) {
		super(threadName);
		// TODO Auto-generated constructor stub
	}

	public MAudioPlayerThread(Runnable runnable, String threadName) {
		super(runnable, threadName);
		// TODO Auto-generated constructor stub
	}

	public MAudioPlayerThread(ThreadGroup group, Runnable runnable) {
		super(group, runnable);
		// TODO Auto-generated constructor stub
	}

	public MAudioPlayerThread(ThreadGroup group, String threadName) {
		super(group, threadName);
		// TODO Auto-generated constructor stub
	}

	public MAudioPlayerThread(ThreadGroup group, Runnable runnable,
			String threadName) {
		super(group, runnable, threadName);
		// TODO Auto-generated constructor stub
	}

	public MAudioPlayerThread(ThreadGroup group, Runnable runnable,
			String threadName, long stackSize) {
		super(group, runnable, threadName, stackSize);
		// TODO Auto-generated constructor stub
	}

	public void SetMusicName(String fileName) {
		m_FileName = fileName;
	}
	
	public void run(){
		
		// Call sexypsf lib to open and play the file
		SexypsfWrapper.sexypsfopen(m_FileName);
        SexypsfWrapper.sexypsfplay();
        
		final int DATA_LEN = MINE_AUDIO_BUFFER_PUT_GET_LEN;
        byte[] audioData = new byte[DATA_LEN];
        int len= 0, ret;
        int counter = 0;
        m_audiotrack.play();
        m_audiotrack.setStereoVolume(1, 1);
        MinePSFPlayerPlayThread play_thread = new MinePSFPlayerPlayThread();
        play_thread.start();
        while(true){
        	if (shallExit) {
        		m_audio_buffer.Discard();
        		break;
        	}
        	ret = SexypsfWrapper.sexypsfputaudiodata(audioData, DATA_LEN);
        	if(ret != DATA_LEN) {
        		// the playback is end, we shall let the play_thread exit
        		// and then let itself exit
        		m_audio_buffer.setAudioBufferEnd();
        		play_thread.interrupt();
        		break;
        	}
        	len = ret;
        	System.out.println("Put audio data to buffer: " + counter++ +" len: " + DATA_LEN);
        	while(len > 0)
        		len -= m_audio_buffer.put(audioData, DATA_LEN-len, len);
        }
        m_audiotrack.stop();
        System.out.println("AudioDataPutThread exit!");
	}
	
	public void SetExit() {
		SexypsfWrapper.sexypsfstop();
		m_audiotrack.pause();
		shallExit = true;
	}
}

/**
 * Audio Data queue
 */
class MineAudioBuffer {
    private byte[] m_buffer;
    private int m_len;
    public int put_index, get_index;
    private Semaphore put, get;
    private boolean m_EndFlag;
    
    public MineAudioBuffer(int len){
    	m_buffer = new byte[len];
    	m_len = len;
    	put_index = 0; get_index = 0;
    	put = new Semaphore(m_len);
    	get = new Semaphore(0);
    	m_EndFlag = false;
    }
    
    public void Discard() {
    	int avail = get.availablePermits();
    	try {
			get.acquire(avail);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
    	put.release(avail);
    }
    
    public boolean getEndFlag() {return m_EndFlag;}
    public void setAudioBufferEnd()
    {
    	m_EndFlag = true;
    }
    
    public int put(byte[] buffer, int index, int len){
    	int avail = put.availablePermits();
    	int put_len = (avail>len) ? (len) : (avail);
    	try {
			put.acquire(put_len);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.arraycopy(buffer, index, m_buffer, put_index, put_len);
		/*
    	for( int i =0; i< put_len; ++i){
    		m_buffer[put_index++] = buffer[index++];
    		if(put_index>=m_len)
    			put_index = 0;
    	}*/
    	get.release(put_len);
    	return put_len;
    }
    
    public int get(byte[] buffer, int index, int len) throws InterruptedException {
    	int get_len;
    	int avail = get.availablePermits();
    	if(avail != 0)
    		get_len = avail>len?len:avail;
    	else
    		get_len = len;
    	try {
			get.acquire(get_len);
		} catch (InterruptedException e) {
			// if it's interrupted, it means there's no more audio data
			throw e;
		}
		System.arraycopy(m_buffer, get_index, buffer, index, get_len);
		/*
    	for(int i=0; i<get_len; ++i){
    		buffer[index++] = m_buffer[get_index++];
    		if(get_index>=m_len)
    			get_index = 0;
    	}
    	*/
    	put.release(get_len);
    	return get_len;
    }
}